/////////////////////////////////////////////////////////////////////////
//
// Amuse Engine SDK - geometry/containment
// Copyright( c) 2015.  All Rights Reserved
//
// File:		AEStaticMeshInstance.h
// Author:		Gianluca Belardelli
// Date:		19/06/2015
//
/////////////////////////////////////////////////////////////////////////
#ifndef _AESTATICMESHINSTANCE_H_
#define _AESTATICMESHINSTANCE_H_

class AEStaticSubmeshInstance;

/// \brief
///   Class representing an instance of static geometry (i.e., typically an instance of the
///   geometry contained in a vmesh file).
/// 
/// A AEStaticMeshInstance object can be created by calling AEStaticMesh::CreateInstance.
/// 
/// AEStaticMeshInstance are the "typical" static scene elements in the Vision engine which
/// make up most of the static part of a scene. 
/// 
/// Additional instances of the same mesh typically take almost no additional memory.
/// 
/// For better rendering and management performance, static mesh instances are split into
/// AEStaticSubmeshInstance objects (each of which corresponds to a VisStaticSubmesh_cl). Only
/// the submesh instances, not the mesh instances, can be rendered and used in visibility
/// determination. This is due to the fact that AEStaticMeshInstance objects can be huge and
/// hold a lot of geometry and materials.
class AEStaticMeshInstance : public AEReferencedObject
{
// Members
public:
	enum AECollisionBehavior
	{
		AECOLLISIONBEHAVIOR_FORCENOCOLLISION	= 0,
		AECOLLISIONBEHAVIOR_FROMFILE 			= 1,
		AECOLLISIONBEHAVIOR_CUSTOM 				= 2
	};

protected:
	friend class AEStaticMesh;
	friend class AEStaticSubmeshInstance;
	
	AEINT32 					m_nNumSubmeshInstances;
	AEStaticSubmeshInstance 	*m_lpSubmeshInstances;
	AEMatrix4f 					m_matTransformation;
	AEBOOL32 					m_bTransformIsIdentity;
	AEBOOL32 					m_bCastStaticShadows;
	
	AEStaticMeshPtr 			m_spMesh;
	AEBoundingBox 				m_bbBoundingBox;
	
	//AEMaterialTextureSetPtr m_spCustomSurfaceSet;
	
	AEUINT32 					m_uiCollisionMask;
	AECollisionBehavior 		m_eCollisionBehavior;
	
	AEVector4f 					m_vcLocalZonePosition; // for repositioning

// Methods
protected:
	void _init( void );
	void _deInit( void );
	void _reInit( void );
	
	AE_DLLIMPEXP AEStaticMeshInstance( void );

public:
	/** @name Costruttori e distruttore */
	/// @{
	/// \brief Distruttore.
	AE_DLLIMPEXP virtual ~AEStaticMeshInstance( void );
	
	/// \brief Non-virtual overload of Release
	AE_DLLIMPEXP void Release( void );
	
	/// @}
	/** @name Bounding Box e Transformazioni */
	/// @{
	
	/// \brief
	///   Sets the world-space bounding box for this static mesh instance (i.e., including all the
	///   submesh instances, after transformation to world space).
	AE_FORCEINLINE void SetBoundingBox( const AEBoundingBox &bbBox );
	
	/// \brief Returns the world-space bounding box for this static mesh instance.
	AE_FORCEINLINE const AEBoundingBox &GetBoundingBox( void ) const;
	
	/// \brief Recomputes the world-space bounding box for this static mesh instance. Call this method whenever the transformation of a mesh instance changes.
	/// \param bAccurate
	///   If set to AEFALSE (default) the bounding boxes are calculated in a performant way by rotating the mesh resource's boxes. In the accurate version
	///   the boxes are generated by the rotated vertices. Naturally, this path is much slower. This is however done by vForge at scene export time so bounding boxes are stored in the most accurate way.
	AE_DLLIMPEXP void ReComputeBoundingBoxes( AEBOOL32 bAccurate=AEFALSE );
	
	/// \brief Sets the world-space transformation matrix for this static mesh instance.
	AE_FORCEINLINE void SetTransform( const AEMatrix4f &matTransform );
	
	/// \brief Returns the world-space transformation matrix of this static mesh instance.
	AE_FORCEINLINE const AEMatrix4f &GetTransform( void ) const;
	
	/// \brief Overridden function return the position locally to the parent zone
	AE_DLLIMPEXP virtual AEBOOL32 GetZoneLocalSpacePosition( AEVector4f &vcDest );
	
	/// @}
	/** @name Gestione Submesh */
	/// @{
	/// \brief Allocates the specified number of submesh instances. 
	/// Use this function if you manually want to create and assign the submesh instances.
	/// Do NOT call this function on instances of static meshes loaded from .vmesh files!
	/// \param iNumInstances Number of submesh instances to allocate.
	AE_DLLIMPEXP void AllocateSubmeshInstances( AEINT32 nNumInstances );
	
	/// \brief Links the submesh instances to the corresponding submeshes and surfaces.
	/// Use this function if the references mesh has been reloaded. In this case
	/// surfaces and submeshes will have been recreated.
	AE_DLLIMPEXP void LinkSubmeshInstances( void );
	
	/// \brief Frees previously allocated submesh instances.
	/// Use this function if you manually want to release the submesh instances.
	/// Do NOT call this function on instances of static meshes loaded from .vmesh files!
	AE_DLLIMPEXP void FreeSubmeshInstances( void );
	
	/// \brief Returns the number of submesh instances in this mesh instance. Has to be identical to the
	///   number of submeshes in the mesh this instance was created from.
	AE_FORCEINLINE AEINT32 GetSubmeshInstanceCount( void ) const;
	
	/// \brief Returns the submesh instance with the specified index.
	AE_FORCEINLINE AEStaticSubmeshInstance *GetSubmeshInstance( AEINT32 nInstanceIndex ) const;

	/// @}
	/** @name Visibilit&agrave; */
	/// @{
	/// \brief Assigns all submesh instances of this static mesh instance to visibility zones.
	/// Note that the submesh instance may end up in different visibility zones.
	/// Also note that this is only an approximation of the "correct" assignment. Unlike for dynamic
	/// scene elements which can rely on their spatial relation to static scene elements for their
	/// visibility assignment, this is not always possible for static geometry!
	/// In order to get correct results, manually assign static mesh instances to visibility zones
	/// using the functionality in the VisVisibilityZone_cl class.
	AE_DLLIMPEXP void AssignToVisibilityZones( void );
	
	/// \brief Removes all submesh instances of this static mesh instance from their respective visibility
	///   zones.
	AE_DLLIMPEXP void RemoveFromVisibilityZones( void );
	
	/// \brief calls SetClipSettings on all submesh instances
	AE_FORCEINLINE void SetClipSettings( AEFLOAT32 fNear, AEFLOAT32 fFar, const AEVector4f *lpvcClipRefPosition );
	
	/// \brief calls SetClipMode on all submesh instances
	AE_FORCEINLINE void SetClipMode( AEINT32 nFlags = AELODTEST_CLIPPOSITION );
	
	/// \brief Restore the clip settings that were in the vmesh file
	AE_FORCEINLINE void SetDefaultClipSettings( const AEVector4f *lpvcClipRefOffset = AENULL );
	
	/// \brief Helper function to call VisStaticGeometryInstance_cl::SetVisibleBitmask on all submeshes. 
	/// In general the visibility bitmask is used to enable/disable the visibility of geometry in specific render contexts. Each render context
	/// has a render filter bitmask (see VisRenderContext_cl::SetRenderFilterMask). Geometry is only rendered when the logical AND operation between these 
	/// two masks is non-zero. Accordingly setting this visible bitmask to 0 will hide geometry in all contexts.
	/// This is the most efficient way to hide objects.
	/// The visible bitmask is a property of each submesh instance (which inherits VisStaticGeometryInstance_cl::SetVisibleBitmask).
	/// This is a helper function to loop through all submeshes and assign a new bitmask to each. But in addition the bitmask for each submesh is ANDed
	/// with the default visibility bitmask that is defined in the submesh resource. This however is 0xffffffff by default, so usually the mask passed
	/// to this function is applied to each submesh. See AE_FORCEINLINE function implementation at the end of this header file.
	/// This is what happens in vForge when the shape property "VisibleBitmask" changes.
	/// Since this bitmask is not stored in this class, there is no getter for the bitmask.
	/// \param iMask
	///   New bitmask applied to all submeshes
	/// \sa VisRenderContext_cl::SetRenderFilterMask
	/// \sa VisStaticGeometryInstance_cl::SetVisibleBitmask
	AE_FORCEINLINE void SetVisibleBitmask( AEUINT32 uiMask );
	
	/// @}
	/** @name Collisioni */
	/// @{
	/// \brief
	///   Specifies the collision bitmask/ collision behavior for the static mesh instance.
	///   (eCollisionBehavior currently only used for Havok Physics.)
	AE_DLLIMPEXP void SetCollisionBitmask( AEUINT32 uiCollisionMask, AECollisionBehavior eCollisionBehavior = AECOLLISIONBEHAVIOR_FROMFILE );
	
	/// \brief Returns the collision bitmask for the static mesh instance.
	AE_FORCEINLINE AEUINT32 GetCollisionBitmask( void ) const;
	
	/// \brief
	///   Specifies the collision behavior for the static mesh instance. Please note that this method will only reset the member variable.
	///   Use the method SetCollisionBitmask() to also apply the value to the engine. 
	///   (eCollisionBehavior currently only used for Havok Physics.)
	AE_DLLIMPEXP void SetCollisionBehavior( AECollisionBehavior eCollisionBehavior );
	
	/// \brief Returns the collision behavior for the static mesh instance.
	///   (Currently only used for Havok Physics.)
	AE_FORCEINLINE AECollisionBehavior GetCollisionBehavior( void ) const;
	
	/// @}
	/** @name Ombre e Illuminazione */
	/// @{
	/// \brief Removes static lightmap information from this
	AE_DLLIMPEXP virtual void ResetStaticLighting( void );
	
	/// \brief Returns true if this static mesh instance is lightmapped.
	AE_DLLIMPEXP AEBOOL32 IsLightmapped( void ) const;
	
	/// \brief Returns whether any of the submeshes in this static mesh instance casts static shadows.
	AE_FORCEINLINE AEBOOL32 GetCastStaticShadows( void ) const;
	
	/// \brief Specifies whether any of the submeshes in this static mesh instance casts static shadows.
	AE_FORCEINLINE void SetCastStaticShadows( AEBOOL32 bStatus );

	/// @}
	/** @name Accesso alla Mesh */
	/// @{
	/// \brief
	///   Returns the static mesh this static mesh instance was created from.
	AE_FORCEINLINE AEStaticMesh *GetMesh( void ) const;
	
	/// \brief Assigns a custom set of materials to this instance
	/// The passed set must be an array of surfaces where the surface count matches the number of surfaces in the original mesh GetMesh()->GetSurfaceCount().
	/// A compatible set can be created via VisSurfaceTextureSet_cl::CreateForMesh where the passed bCreateSrfInstances parameter must be set to true (unlike for entities).
	/// A AENULL poiner can be passed to reset to the materials that are defined by the mesh. A material set can be shared across instances.
	/// This feature is just a higher level convenience function for setting surface pointers on the submesh instances directly, which is also possible through VisStaticGeometryInstance_cl::SetSurface.
	/// The applied set is serialized along with static meshes when serializing to VArchive.
	/// \param pSet A custom surface set that is compatible with the owner mesh resource of this instance
	/// \sa VisSurfaceTextureSet_cl
	/// \sa VisSurfaceTextureSet_cl::CreateForMesh
	/// \sa VisStaticGeometryInstance_cl::SetSurface
	//AE_DLLIMPEXP void SetCustomMaterialSet( VisSurfaceTextureSet_cl *pSet );
	
	/// \brief Returns the custom surface set instance that has been set via SetCustomSurfaceSet. By default this is AENULL which means this instance uses the materials defined by the mesh
	//AE_FORCEINLINE VisSurfaceTextureSet_cl* GetCustomMaterialSet() const {return m_spCustomSurfaceSet;}
	/// @}
};

#include "AEStaticMeshInstance.inl"

#endif // _AESTATICMESHINSTANCE_H_
